package service;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;

import org.apache.http.NameValuePair;
import org.apache.http.client.utils.URLEncodedUtils;

import com.sun.net.httpserver.HttpServer;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpExchange;
import common.Constants;

import java.net.URI;
import java.net.InetSocketAddress;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerException;

import org.apache.commons.daemon.Daemon;
import org.apache.commons.daemon.DaemonContext;
import org.apache.commons.daemon.DaemonInitException;

import org.apache.log4j.Logger;
import org.apache.lucene.document.Document;
import org.apache.lucene.index.CorruptIndexException;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.SimpleFSDirectory;
import org.wltea.analyzer.lucene.IKQueryParser;

import search.ResultFormatter;


public class SearcherService implements Daemon {
	
	private final Logger logger = Logger.getLogger(SearcherService.class); 
	
	private int _port = Integer.parseInt(GlobalConfiguration.getInstance().get(
			Constants.CONF_SEARCH_PORT));

	@Override
	public void destroy() {
		logger.info("Search service destroyed.");
	}

	@Override
	public void init(DaemonContext arg0) throws DaemonInitException, Exception {
		logger.info("Search service initializing...");
	}

	@Override
	public void start() throws Exception {
		logger.info("Search service starts");
		HttpServer hs = HttpServer.create(new InetSocketAddress(_port),0);
		hs.createContext("/search", new SearchHandler());
		hs.setExecutor(null);
		hs.start();
	}

	@Override
	public void stop() throws Exception {
		logger.info("Search service stops");
	}

	class SearchHandler implements HttpHandler {

		private static final String PARAM_QUERY = "q";
		
		private static final int DEFAULT_HIT = 10;

		private final Logger logger = Logger.getLogger(SearchHandler.class);

		private final String _indexDirPath = GlobalConfiguration.getInstance()
				.get(Constants.CONF_INDEX_PATH);

		private final String _encoding = GlobalConfiguration.getInstance().get(
				Constants.CONF_QUERY_ENC);

		private IndexSearcher _searcher = null;

		public SearchHandler() {
			try {
				Directory dir = new SimpleFSDirectory(new File(_indexDirPath),
						null);
				_searcher = new IndexSearcher(dir);
			} catch (IOException e) {
				logger.error("{}", e);
			}
		}

		@Override
		public void handle(HttpExchange msg) {

			URI uri = msg.getRequestURI();

			// Parse parameters
			HashMap<String, String> parameters = parseParameters(uri);
			
			Query query;
			try {
				// Query Process
				query = makeQuery(parameters);
				
				// Do search
				TopDocs hits = doSearch(query);
				
				//Generate response 
				response(msg, hits);
				
			} catch (IOException e) {
				logger.error("{}", e);
			}		

		}

		private HashMap<String, String> parseParameters(URI uri) {

			logger.info("Parsing uri : " + uri.toString());

			HashMap<String, String> params = new HashMap<String, String>();

			List<NameValuePair> paramlist = URLEncodedUtils.parse(uri,
					_encoding);
			
			for (int i = 0; i < paramlist.size(); ++i) {
				NameValuePair param = paramlist.get(i);
				String name = param.getName();
				String value = param.getValue();
				params.put(name, value);
			}
			
			return params;
		}

		private Query makeQuery(HashMap<String, String> params) throws IOException {
			
			Query query = null;
			
			if (params.containsKey(PARAM_QUERY)) {
				String rawQuery = params.get(PARAM_QUERY);
				query = IKQueryParser.parse(Constants.INDEX_FIELD_TITLE, rawQuery);

			}else{
				logger.warn("No query parameter(" + PARAM_QUERY + ")");
			}
			
			return query;

		}

		private TopDocs doSearch(Query query) throws IOException {
			logger.info("Do search for query : " + query.toString());
			return _searcher.search(query, DEFAULT_HIT);
		}
		
		private void response(HttpExchange msg, TopDocs hits) throws CorruptIndexException, IOException{
			logger.info("Generating response");
			
			ArrayList<String> fields = new ArrayList<String>();
			fields.add(Constants.INDEX_FIELD_TITLE);
			
			String result = "";
			try {
				result = (new ResultFormatter()).XmlFormat(_searcher, hits, fields);
			} catch (ParserConfigurationException e) {
				logger.error("{}", e);
			} catch (TransformerException e) {
				logger.error("{}", e);
			}

			byte[] response = "".getBytes();

			response = result.getBytes("utf-8");
			
			System.out.println(response.length);
			
			msg.sendResponseHeaders(200, response.length);
			OutputStream os = msg.getResponseBody();
			os.write(response);
			os.close();
			
		}

	}

}
